import java.util.ArrayList;
import java.util.Collections;
import java.util.Timer;
import java.util.TimerTask;

public class TitleBarInfo
{
    private class InfoMessage
    {
        private String InfoText;
        private int InfoTicksRemaining;
        private int ActivationTicksRemaining;
        private int ShowPendingTick;
        private boolean ShowWaitCursorUntilRemoved;

        public InfoMessage(String InfoText, boolean AutoRemove, boolean DelayedActivation, boolean ShowPending, boolean ShowWaitCursorUntilRemoved)
        {
            if (InfoText.length() < 1)
            {
                System.out.println("error in TitleBarInfo.InfoElement(): passed value not useful!");
            }
            if (InfoText.length() > 1000)
            {
                InfoText = InfoText.substring(0, 1000) + "[...]";
                System.out.println("error in TitleBarInfo.InfoElement(): passed value not useful!");
            }

            this.InfoText = InfoText;

            if (AutoRemove)
                this.InfoTicksRemaining = 3 * 10; // 3 seconds
            else
                this.InfoTicksRemaining = Integer.MAX_VALUE;

            if (DelayedActivation)
                this.ActivationTicksRemaining = 1 * 10; // 1 second
            else
                this.ActivationTicksRemaining = 0;

            if (ShowPending)
                this.ShowPendingTick = 0;
            else
                this.ShowPendingTick = -1;

            this.ShowWaitCursorUntilRemoved = ShowWaitCursorUntilRemoved;
        }
    }

    private ArrayList<InfoMessage> InfoMessages;

    private Timer TickTimer = null;
    private TimerTask TickTimerTask;

    private MainWindow MainWindow;
    private String MainWindowBaseTitle;

    private int ShowWaitCursorCounter = 0;

    // IMPORTANT: assume public methods get called concurrently from different
    // threads, especially as a result of the user doing mouse/keyboard input AND
    // from the Java redraw thread - DO NOT USE ANY LOCKING (MUTEX ETC.)!
    // System has deadlocked in tests when having used a ReentrantLock.

    public TitleBarInfo(MainWindow MainWindow, String MainWindowBaseTitle)
    {
        InfoMessages = new ArrayList<InfoMessage>();

        this.MainWindow = MainWindow;
        this.MainWindowBaseTitle = MainWindowBaseTitle;

        TickTimerTask = new TimerTask()
        {
            @Override
            public void run()
            {
                TitleBarInfoTick();
            }
        };

        TickTimer = new Timer();
        TickTimer.schedule(TickTimerTask, 0, 100); // less than 10 times per second was too sluggish (confusing)
    }

    public void AddInfoText(String InfoText, boolean AutoRemove, boolean DelayedActivation, boolean ShowPending, boolean ShowWaitCursorUntilRemoved)
    {
        // check if already existing (allowed once only)
        for (int i = 0; i < InfoMessages.size(); i++)
        {
            if (InfoMessages.get(i).InfoText.equals(InfoText)) // do not compare StayUntilRemoved, not useful
            {
                return;
            }
        }

        // add
        InfoMessage NewInfoElement = new InfoMessage(InfoText, AutoRemove, DelayedActivation, ShowPending, ShowWaitCursorUntilRemoved);
        for (int i = 0; i < InfoMessages.size(); i++)
        {
            // try to re-use an obsolete (removed) message location; so we don't need to
            // remove elements from ArrayList<>, which has caused problems when
            // an other thread has removed messages at the same time

            if (InfoMessages.get(i).InfoText.length() == 0)
            {
                InfoMessages.set(i, NewInfoElement);
                return;
            }
        }
        InfoMessages.add(NewInfoElement);
        return;
    }

    public void RemoveInfoText(String InfoText)
    {
        for (int i = 0; i < InfoMessages.size(); i++)
        {
            if (InfoMessages.get(i).InfoText.length() > 0)
            {
                String InfoTextWithoutPending = InfoMessages.get(i).InfoText;

                if (InfoTextWithoutPending.contains("☺")) // progress shown?
                    InfoTextWithoutPending = InfoTextWithoutPending.substring(0, InfoTextWithoutPending.length() - (1 + 3));

                if (InfoTextWithoutPending.equals(InfoText))
                {
                    if (InfoMessages.get(i).ShowWaitCursorUntilRemoved)
                    {
                        ShowDefaultCursor(); // not clear what to do if there would be SEVERAL show-wait-cursor elements, just don't add several ones (would not make sense anyway)
                    }

                    InfoMessages.get(i).InfoText = "";
                }
            }
        }
    }

    public void RemoveInfoTextContaining(String InfoTextPart)
    {
        for (int i = 0; i < InfoMessages.size(); i++)
        {
            if (InfoMessages.get(i).InfoText.length() > 0)
            {
                if (InfoMessages.get(i).InfoText.toLowerCase().contains(InfoTextPart.toLowerCase()))
                {
                    if (InfoMessages.get(i).ShowWaitCursorUntilRemoved)
                    {
                        ShowDefaultCursor(); // not clear what to do if there would be SEVERAL show-wait-cursor elements, just don't add several ones (would not make sense anyway)
                    }

                    InfoMessages.get(i).InfoText = "";
                }
            }
        }
    }

    private void ShowDefaultCursor()
    {
        if (ShowWaitCursorCounter >= 1)
            ShowWaitCursorCounter--;
        // else
        // System.out.println("warning in TitleBarInfo.ShowDefaultCursor(): ShowWaitCursorCounter value not useful - more often wait cursor reset than set!");
        if (ShowWaitCursorCounter == 0)
        {
            CursorManager.SetWaitCursorEnabled(false);
        }
    }

    private void ShowWaitCursor()
    {
        ShowWaitCursorCounter++;
        if (ShowWaitCursorCounter >= 1)
        {
            CursorManager.SetWaitCursorEnabled(true);
        }
    }

    private void TitleBarInfoTick()
    {
        for (int i = 0; i < InfoMessages.size(); i++)
        {
            if (InfoMessages.get(i).InfoText.length() > 0)
            {
                if (InfoMessages.get(i).ActivationTicksRemaining >= 1)
                {
                    // WAIT for displaying message...

                    InfoMessages.get(i).ActivationTicksRemaining--;
                    if (InfoMessages.get(i).ActivationTicksRemaining == 0)
                    {
                        if (InfoMessages.get(i).ShowWaitCursorUntilRemoved)
                        {
                            ShowWaitCursor(); // checks if already set to save CPU time
                        }
                    }
                }
                else if (InfoMessages.get(i).InfoTicksRemaining <= 0)
                {
                    // message is being displayed and now to be removed as displaying time span over

                    if (InfoMessages.get(i).ShowWaitCursorUntilRemoved)
                    {
                        ShowDefaultCursor(); // not clear what to do if there would be SEVERAL show-wait-cursor elements, just don't add several ones (would not make sense anyway)
                    }
                    InfoMessages.get(i).InfoText = "";
                }
                else
                {
                    // message is being displayed, update it if necessary

                    if (InfoMessages.get(i).InfoTicksRemaining != Integer.MAX_VALUE) // ELSE if!
                        InfoMessages.get(i).InfoTicksRemaining--;

                    if (InfoMessages.get(i).ShowPendingTick >= 0) // enabled?
                        InfoMessages.get(i).ShowPendingTick++;
                    if (InfoMessages.get(i).ShowPendingTick >= 2 * 10)
                        InfoMessages.get(i).ShowPendingTick = 0; // cycle
                }
            }
        }

        UpdateTitleBarInfo();
    }

    public void UpdateTitleBarInfo()
    {
        ArrayList<String> TitleBarInfoMessages = new ArrayList<String>();

        for (int i = 0; i < InfoMessages.size(); i++)
        {
            if (InfoMessages.get(i).InfoText.length() > 0)
            {
                if (InfoMessages.get(i).ActivationTicksRemaining <= 0)
                {
                    String TitleBarInfoMessage = InfoMessages.get(i).InfoText;

                    if (InfoMessages.get(i).ShowPendingTick >= 0)
                    {
                        switch ((int) (InfoMessages.get(i).ShowPendingTick / 10))
                        {
                        /*
                        case 0:
                        	TitleBarInfoMessage += " ☻☺☺";
                        	break;
                        case 1:
                        	TitleBarInfoMessage += " ☺☻☺";
                        	break;
                        case 2:
                        	TitleBarInfoMessage += " ☺☺☻";
                        	break;
                        }
                        */
                        case 0:
                            TitleBarInfoMessage += " ☻☺";
                            break;
                        case 1:
                            TitleBarInfoMessage += " ☺☻";
                            break;
                        }
                    }

                    TitleBarInfoMessages.add(TitleBarInfoMessage);
                }
            }
        }

        Collections.sort(TitleBarInfoMessages); // https://www.javatpoint.com/how-to-sort-arraylist-in-java

        String TitleBarInfoString = "";

        for (int m = 0; m < TitleBarInfoMessages.size(); m++)
            TitleBarInfoString += " ~ " + TitleBarInfoMessages.get(m);

        if (!(MainWindow.getTitle().equals(MainWindowBaseTitle + TitleBarInfoString)))
            MainWindow.setTitle(MainWindowBaseTitle + TitleBarInfoString);
    }
}
